package dbs

import (
	"ManageServer/conf"
	"ManageServer/utils/gls"
	"ManageServer/utils/logger"
	"errors"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mssql"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"time"
)

var DB *gorm.DB
var SqlDB *gorm.DB

func InitSql() {
	// 从配置文件中获取数据库配置
	engine, _ := conf.ConfFile.GetValue("sql", "engine")
	tryOpen := 5
	for tryOpen > 0 {
		tryOpen--
		if tryOpen == 0 {
			logger.ErrorLogger.Error("数据库连接失败")
			return
		}

		err := OpenDB(engine)
		if err != nil {
			logger.ErrorLogger.Error(err.Error())
			time.Sleep(2 * time.Second)
		}
		break
	}

	if err := InitSqlServer(); err != nil {
		logger.ErrorLogger.Error("sql server 连接失败: " + err.Error())
		return
	}
	logger.ErrorLogger.Info("sql server 已连接")

	logger.InfoLogger.Info("数据库已连接")
}

// 打开数据库连接
func OpenDB(engine string) (err error) {
	err = errors.New("不支持的数据库类型")

	if engine == "mysql" {
		username, _ := conf.ConfFile.GetValue("mysql", "username")
		database, _ := conf.ConfFile.GetValue("mysql", "database")
		password, _ := conf.ConfFile.GetValue("mysql", "password")
		host, _ := conf.ConfFile.GetValue("mysql", "host")
		port, _ := conf.ConfFile.GetValue("mysql", "port")
		charset, _ := conf.ConfFile.GetValue("mysql", "charset")
		parseTime, _ := conf.ConfFile.GetValue("mysql", "parseTime")
		loc, _ := conf.ConfFile.GetValue("mysql", "loc")

		DB, err = gorm.Open("mysql", username+":"+password+"@tcp("+host+":"+port+")/"+database+"?charset="+charset+"&parseTime="+parseTime+"&loc="+loc)
	}
	if engine == "sqlite" {
		path, err := conf.ConfFile.GetValue("sqlite", "path")
		if err != nil {
			return err
		}

		DB, err = gorm.Open("sqlite3", path)

	}

	DB.DB().SetMaxIdleConns(1024)
	DB.DB().SetMaxOpenConns(256)
	DB.DB().SetConnMaxLifetime(time.Hour)
	DB.LogMode(true)

	return
}

func InitSqlServer() error {
	var err error
	username, _ := conf.ConfFile.GetValue("sqlServer", "username")
	database, _ := conf.ConfFile.GetValue("sqlServer", "database")
	password, _ := conf.ConfFile.GetValue("sqlServer", "password")
	host, _ := conf.ConfFile.GetValue("sqlServer", "host")

	SqlDB, err = gorm.Open("mssql", "sqlserver://"+username+":"+password+"@"+host+":1433?database="+database+" ")
	if err != nil {
		return errors.New("init sql is er ")
	}
	return nil
}

type D_CB func(DB **gorm.DB) (err error)

func D_Pool(cb D_CB) (err error) {
	// 获取当前 goroutine 信息
	//获取当前数据库连接
	var db *gorm.DB
	var db_pramar **gorm.DB
	db_interface := gls.Get("DB")
	if db_interface == nil {
		// 不存在则新建连接
		db = DB
		// 置入 goroutine
		// 问题一、以地址传入，否则传入的将是一个 db 的克隆
		// 问题二、传入的是这个初始化的 db,而不是执行过 begin 的db,是不是需要重定义 begin？
		// 方案一：内存中存储 db 地址，无论其他地方如何使用，都改动的是地址指向的值
		// 方案二：重定义 db 的 begin 、commit 等方法，在完成操作后重新将 db 的克隆写入内存
		// 此处采用方案一
		db_pramar = &db
		gls.Put("DB", db_pramar)

	} else {
		// 获取对应 goroutine DB
		// 拿出来的 是一个 db 地址
		db_pramar = db_interface.(**gorm.DB)
	}
	return cb(db_pramar)
}

func D_Transaction(cb D_CB) (err error) {
	var db *gorm.DB
	var db_pramar **gorm.DB
	db_interface := gls.Get("DB")

	if db_interface == nil {
		db = DB
		db_pramar = &db
		gls.Put("DB", db_pramar)

	} else {
		db_pramar = db_interface.(**gorm.DB)
	}

	// b = a.Begin()
	// a,b 的类型已经发生改变， a 是 sqlDb (拥有 begin 方法) ; b 是 sqlTx (拥有 commit,rollback 方法)
	// 所以即是通过 commit 提交完数据，也不能用 b.Begin 再次开启事务
	(*db_pramar) = (*db_pramar).Begin()

	//DO
	err = cb(db_pramar)

	if err != nil {
		(*db_pramar).Rollback()
		return
	}

	(*db_pramar).Commit()
	return
}
